package com.education.wisdom.controller.wxpay;

import com.education.common.utils.IpUtils;
import com.education.common.utils.ResultMap;
import com.education.common.utils.UUIDUtils;
import com.education.wisdom.feign.UserFeign;
import com.education.wisdom.pojo.PaymentRecord;
import com.education.wisdom.service.IPaymentRecordService;
import com.education.wisdom.service.IUserAccountService;
import com.github.wxpay.sdk.WXPay;
import com.github.wxpay.sdk.WXPayUtil;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * 微信支付 - 通用API.
 *
 * <p>
 * 类似支付宝中的条码支付.
 *
 * @author Mengday Zhang
 * @version 1.0
 * @since 2018/6/15
 */
@Slf4j
@RestController
@RequestMapping("/wxpay")
public class WXPayController {

    @Autowired
    private WXPay wxPay;
    @Autowired
    private WXPayClient wxPayClient;
    @Autowired
    private MyWXPayConfig wxPayAppConfig;
    @Autowired
    private IPaymentRecordService paymentRecordService;
    @Autowired
    private IUserAccountService accountService;
    @Autowired
    private UserFeign userFeign;


    /**
     * 微信统一下单接口
     *
     * @param orderNo 订单号
     * @param amount  订单金额
     * @param body    商品名称
     * @param request
     * @return
     */
    @ApiOperation(value = "统一下单", notes = "统一下单")
    @GetMapping("/unifiedOrder")
    public ResultMap unifiedOrder(
            @ApiParam(value = "订单号") @RequestParam String orderNo,
            @ApiParam(value = "订单金额") @RequestParam double amount,
            @ApiParam(value = "商品名称") @RequestParam String body,
            HttpServletRequest request) {
        try {
            // 1、验证订单是否存在
            String ip = IpUtils.getIpAddr(request);
            // 2、开始微信支付统一下单
            Map<String, String> returnMap = new HashMap<>(1);
            Map<String, String> responseMap = new HashMap<>(1);
            Map<String, String> requestMap = new HashMap<>(1);
            try {
                WXPay wxpay = new WXPay(wxPayAppConfig);
                requestMap.put("out_trade_no", orderNo);                                // 商户订单号
                requestMap.put("trade_type", "APP");                                    // App支付类型
                requestMap.put("body", body);                                           // 商品描述
                requestMap.put("total_fee", String.valueOf((int) (amount * 100)));      // 总金额
                requestMap.put("spbill_create_ip", ip);                                 // 终端IP
                requestMap.put("notify_url", wxPayAppConfig.getNotifyUrl());            // 接收微信支付异步通知回调地址
                Map<String, String> resultMap = wxpay.unifiedOrder(requestMap);
                //获取返回码
                String returnCode = resultMap.get("return_code");
                String returnMsg = resultMap.get("return_msg");
                //若返回码为SUCCESS，则会返回一个result_code,再对该result_code进行判断
                if ("SUCCESS".equals(returnCode)) {
                    String resultCode = resultMap.get("result_code");
                    String errCodeDes = resultMap.get("err_code_des");
                    if ("SUCCESS".equals(resultCode)) {
                        responseMap = resultMap;
                    }
                }
                if (responseMap == null || responseMap.isEmpty()) {
                    return ResultMap.error("获取预支付交易会话标识失败");
                }
                // 3、签名生成算法
                Long time = System.currentTimeMillis() / 1000;
                String timestamp = time.toString();
                returnMap.put("appid", wxPayAppConfig.getAppID());
                returnMap.put("partnerid", wxPayAppConfig.getMchID());
                returnMap.put("prepayid", responseMap.get("prepay_id"));
                returnMap.put("noncestr", responseMap.get("nonce_str"));
                returnMap.put("timestamp", timestamp);
                returnMap.put("package", "Sign=WXPay");
                //returnMap.put("signtype", "MD5");, WXPayConstants.SignType.MD5
                returnMap.put("sign", WXPayUtil.generateSignature(returnMap, wxPayAppConfig.getKey()));//微信支付签名
                return ResultMap.ok().put("data", returnMap);
            } catch (Exception e) {
                //logger.error("订单号：{}，错误信息：{}", orderNo, e.getMessage());
                return ResultMap.error("微信支付统一下单失败");
            }
        } catch (Exception e) {
            return ResultMap.error("运行异常，请联系管理员");
        }
    }

    /**
     * 微信支付异步通知(弃用)
     *
     * @param request
     * @return
     */
    @RequestMapping(value = "/wxnotify")
    public String wxnotify(HttpServletRequest request) {
        InputStream is = null;
        String xmlBack = "<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[报文为空]]></return_msg></xml> ";
        try {
            /*is = request.getInputStream();
            // 将InputStream转换成String
            BufferedReader reader = new BufferedReader(new InputStreamReader(is));
            StringBuilder sb = new StringBuilder();
            String line = null;
            while ((line = reader.readLine()) != null) {
                sb.append(line + "\n");
            }*/

            try {
                // 转换成map
                //  Map<String, String> resultMap = WXPayUtil.xmlToMap(xmlBack);
                Map<String, String> resultMap = wxPayClient.getNotifyParameter(request);

                WXPay wxpayApp = new WXPay(wxPayAppConfig);
                log.info("回调返回结果：" + resultMap.toString());
                if (wxpayApp.isPayResultNotifySignatureValid(resultMap)) {
                    String returnCode = resultMap.get("return_code");  //状态
                    String outTradeNo = resultMap.get("out_trade_no"); //商户订单号
                    String transactionId = resultMap.get("transaction_id");
                    if (returnCode.equals("SUCCESS")) {
                        if (StringUtils.isNotBlank(outTradeNo)) {
                            PaymentRecord record = paymentRecordService.selectPaymentRecordByRecordId(outTradeNo);

                            if (record != null && record.getStatus().equals("1") == false) {
                                String userId = record.getUserId();
                                BigDecimal curr = record.getOrderAmount();
                                PaymentRecord mentRecord = new PaymentRecord();
                                mentRecord.setRecordId(outTradeNo);
                                mentRecord.setStatus("1");
                                mentRecord.setCompleteTime(new Date());
                                mentRecord.setBillId(transactionId);
                                paymentRecordService.updatePaymentRecord(mentRecord);

                                Map<String, Object> login = userFeign.selectByUserId(Integer.parseInt(userId));
                                if (login.size() > 0) {
                                    Map<String, Object> map = new HashMap<>();
                                    map.put("id", Integer.parseInt(userId));
                                    //用戶現有达人币
                                    BigDecimal price1 = new BigDecimal(login.get("price").toString());
                                    //map.put("price", price1 + price * 10);
                                    map.put("price", price1.add((record.getOrderAmount().multiply(new BigDecimal(10)))));
                                    userFeign.updatePriceByUserId(Integer.parseInt(userId), price1.add((record.getOrderAmount().multiply(new BigDecimal(10)))));
                                }

                                /*UserAccount account = accountService.selectUserAccountByUserId(userId);
                                BigDecimal amount2 = account.getJaAmount();
                                account.setJaAmount(curr.add(amount2));
                                accountService.updateUserAccount(account);*/
                            }
                            xmlBack = "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>";
                        }
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            return xmlBack;
        } catch (Exception e) {
        } finally {
            if (is != null) {
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return xmlBack;
    }

    @RequestMapping(value = "/notify")
    public String wxPayNotify(HttpServletRequest request) {
        String resXml = "";
        try {
            InputStream inputStream = request.getInputStream();
            //将InputStream转换成xmlString
            BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
            StringBuilder sb = new StringBuilder();
            String line = null;
            try {
                while ((line = reader.readLine()) != null) {
                    sb.append(line + "\n");
                }
            } catch (IOException e) {
                System.out.println(e.getMessage());
            } finally {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            resXml = sb.toString();
            String result = wxPayClient.payBack(resXml);
            return result;
        } catch (Exception e) {
            System.out.println("微信手机支付失败:" + e.getMessage());
            String result = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>" + "<return_msg><![CDATA[报文为空]]></return_msg>" + "</xml> ";
            return result;
        }
    }

    @ApiOperation(value = "退款", notes = "退款")
    @PostMapping("/wxrefund")
    public ResultMap wxrefund(@ApiParam(value = "订单号") @RequestParam String orderNo,
                              @ApiParam(value = "退款金额") @RequestParam double amount,
                              @ApiParam(value = "退款原因") @RequestParam(required = false) String refundReason) {
        if (StringUtils.isBlank(orderNo)) {
            return ResultMap.error("订单编号不能为空");
        }
        if (amount <= 0) {
            return ResultMap.error("退款金额必须大于0");
        }

        Map<String, String> responseMap = new HashMap<>();
        Map<String, String> requestMap = new HashMap<>();
        WXPay wxpay = new WXPay(wxPayAppConfig);
        requestMap.put("out_trade_no", orderNo);
        requestMap.put("out_refund_no", UUIDUtils.getByUUId());
        requestMap.put("total_fee", "订单支付时的总金额，需要从数据库查");
        requestMap.put("refund_fee", String.valueOf((int) (amount * 100)));//所需退款金额
        requestMap.put("refund_desc", refundReason);
        try {
            responseMap = wxpay.refund(requestMap);
        } catch (Exception e) {
            e.printStackTrace();
        }
        String return_code = responseMap.get("return_code");   //返回状态码
        String return_msg = responseMap.get("return_msg");     //返回信息
        if ("SUCCESS".equals(return_code)) {
            String result_code = responseMap.get("result_code");       //业务结果
            String err_code_des = responseMap.get("err_code_des");     //错误代码描述
            if ("SUCCESS".equals(result_code)) {
                //表示退款申请接受成功，结果通过退款查询接口查询
                //修改用户订单状态为退款申请中或已退款。退款异步通知根据需求，可选
                //
                return ResultMap.ok("退款申请成功");
            } else {
                // logger.info("订单号:{}错误信息:{}", orderNo, err_code_des);
                return ResultMap.error(err_code_des);
            }
        } else {
            //      logger.info("订单号:{}错误信息:{}", orderNo, return_msg);
            return ResultMap.error(return_msg);
        }
    }

    /**
     * 订单查询
     *
     * @param orderNo
     * @return
     * @throws Exception
     */
    @GetMapping("/orderQuery")
    public Object orderQuery(String orderNo) throws Exception {
        Map<String, String> data = new HashMap<>();
        data.put("out_trade_no", orderNo);
        Map<String, String> result = wxPay.orderQuery(data);

        return result;
    }

    /**
     * 退款
     * 注意：调用申请退款、撤销订单接口需要商户证书
     * 注意：沙箱环境响应结果可能会是"沙箱支付金额不正确,请确认验收case"，但是正式环境不会报这个错误
     * 微信支付的最小金额是0.1元，所以在测试支付时金额必须大于0.1元，否则会提示微信支付配置错误，可以将microPay的total_fee大于1再退款
     */
    @PostMapping("/refund")
    public Object refund(String orderNo) throws Exception {
        Map<String, String> reqData = new HashMap<>();
        // 商户订单号
        reqData.put("out_trade_no", orderNo);
        // 授权码
        reqData.put("out_refund_no", orderNo);
        // 订单总金额，单位为分，只能为整数
        reqData.put("total_fee", "1010");
        //退款金额
        reqData.put("refund_fee", "2");
        // 退款异步通知地址
        reqData.put("notify_url", wxPayAppConfig.getNotifyUrl());
        reqData.put("refund_fee_type", "CNY");
        reqData.put("op_user_id", wxPayAppConfig.getMchID());

        Map<String, String> resultMap = wxPay.refund(reqData);
        log.info(resultMap.toString());

        return resultMap;
    }


    /**
     * 退款结果通知
     * <p>
     * 特别说明：退款结果对重要的数据进行了加密，商户需要用商户秘钥进行解密后才能获得结果通知的内容
     *
     * @param request
     * @throws Exception
     */
    @RequestMapping("/refund/notify")
    public String refundNotify(HttpServletRequest request) throws Exception {

//        Map<String, String> notifyMap = new HashMap<>();
//        notifyMap.put("nonce_str", "9b4e428ae262d5dca96178027e849fa9");
//        notifyMap.put("req_info", "VKGj8c81RwQ45LcyWEVBE9/HsNfADGbgmbIAQZ2ydcbIFhIIcJFKFQwGfcSGgFGtQlWvg6KDNsRjmCjN+PvipJ3roynJ7cME0LOFG50VGtk4EYHqdjFzUVANI7GpT+i6Ok+ZWivH0MwoGK2fsz3WG+bYs2XJBwav/K89tKjFhZGitCKKBeGqcP99fa/gAL0swNXXNQHmL806Zi+QcACzL3E89BtP9FlXM2Gi+wPQafvPr+/iE+LrPdMlNUa5LiZnenZXUF24kMdhaTafczcKL4sZjRXQHEfEyc/pIZPbIjcNIETvHsskyzKuHVr/SAFkxaM6RR1Kl9pyWZGUjkH5SOeqsT8uL7YQmTlDXrnXmno3AvZdnepTGL5w69yIOmQNPeMqsd01ES9WX36GZYOigfi2+BJ9RRXjIffmpB/MFF+zryyvLTaJE2obCwFSHrmOD8YbaJqrZXOUvWZQrn7wIQgaCypo8V57cD3w5d2RSgIHNrdnEDYlbRcLNYgKuL+T9+1HPhU/frowZgwPN9IB53OahZV3p1Yvos23kvhqPCLn3BYgUihRbey6QhEtL2QyifiQ9e8WVLzWpRZ+DOa8VrhYvSuTfjRdjoNanqHFvXGP6uEsEa+DETqnexpB7xOS9m/CdmlNCwbdUplPEVzNQQdzYT4kybi00Y8A+EdairxfVyK9A7MAYAMtAO9yxV2ht0bn3SofFyZe/YSzdJgxdtcxBf1CVYN6x+yHcSueCSgq4cM/2VCwh4J1+pUVmNpEm0OVcdKbV5USkaxJR0h7Yd+n5FTz5Q2S/qvyDo202cUzLFPI5UqQm5X+FOrWDAkmmr5yVcDQIm3dAdb31jkz0X2TPYt5g7ciQ1h9heyVxJ67FexKvEM4pKCCubtWx6nyxcOUghHMrh8DSoBtewtNjbnwGVIbLsSb6X9MIYAkWIDbqNVP1f63GiZU+cJlhBmvcb8aeQUdTTj7EX5pOTIVSVv5D6SkKmpGU4FGvV+WjufuGX4ZRZo+01p6xl0sfZVmucG1UtxhX6bMCJb06yDwxpv7tGwkwS4TCK4SQp40Xe0=");
//        notifyMap.put("appid", "xxx");
//        notifyMap.put("mch_id", "xxx");
//        notifyMap.put("return_code", "SUCCESS");

        // 注意：同样的通知可能会多次发送给商户系统。商户系统必须能够正确处理重复的通知。
        // 推荐的做法是，当收到通知进行处理时，首先检查对应业务数据的状态，判断该通知是否已经处理过，如果没有处理过再进行处理，如果处理过直接返回结果成功。
        // 在对业务数据进行状态检查和处理之前，要采用数据锁进行并发控制，以避免函数重入造成的数据混乱。
        // TODO 处理业务
        Map<String, String> requstInfoMap = wxPayClient.decodeRefundNotify(request);

        // 商户处理退款通知参数后同步返回给微信参数
        Map<String, String> responseMap = new HashMap<>();
        responseMap.put("return_code", "SUCCESS");
        responseMap.put("return_msg", "OK");
        String responseXml = WXPayUtil.mapToXml(responseMap);
        return responseXml;
    }

    /**
     * 退款查询
     *
     * @param orderNo
     * @return
     * @throws Exception
     */
    @GetMapping("/refundQuery")
    public Object refundQuery(String orderNo) throws Exception {
        Map<String, String> reqData = new HashMap<>();
        reqData.put("out_trade_no", orderNo);
        Map<String, String> result = wxPay.refundQuery(reqData);

        return result;
    }


    /**
     * 下载对账单
     * 注意：
     * 微信在次日9点启动生成前一天的对账单，建议商户10点后再获取；
     * 对账单接口只能下载三个月以内的账单。
     * 下载对账单：https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_6
     *
     * @throws Exception
     */
    @PostMapping("/downloadBill")
    public Object downloadBill(String billDate) throws Exception {
        Map<String, String> reqData = new HashMap<>();
        reqData.put("bill_date", billDate);
        reqData.put("bill_type", "ALL");
        Map<String, String> resultMap = wxPay.downloadBill(reqData);

        return resultMap;
    }


    /**
     * 获取沙箱环境API秘钥，
     * <p>
     * 这里只是为了获取，可以放在main方法下运行，这里作为api来运行的，实际情况下不应该暴露出来
     *
     * @return
     * @throws Exception
     */
    @GetMapping("/sandbox/getSignKey")
    public Object getSignKey() throws Exception {
        Map<String, String> signKey = wxPayClient.getSignKey();
        log.info(signKey.toString());

        return signKey;
    }

}
